home *** CD-ROM | disk | FTP | other *** search
- /*
- * tcpf.c -- TCP to stdio filter
- *
- * July 1987, Kirk Lougheed
- *
- * Copyright (c) 1987 by cisco Systems, Inc.
- *
- * This program is designed to read and write from standard input and
- * output to a raw TCP socket. The specific application is sending data
- * to printers attached to a terminal server's serial or parallel ports.
- *
- * This program may be used in a shell script. The following example is
- * of a "print" command that sends text to a PostScript printer.
- *
- * /usr/local/bin/lptops -2 -o -ntr $* | /usr/local/bin/tcpf -e chaff 4008 &
- *
- * To use it to stuff a file at a gateway called wilma, for instance, use
- * the command line:
- *
- * tcpf -t wilma 23 <doit
- *
- * where the file doit contains, for instance:
- *
- * foo^M
- * ena
- * foobar^M
- * ping
- * ip^Mnit^M100^M^M^M^M
- * disa
- * exit
- *
- * This script will log onto wilma, get into enable mode, then send 100 pings
- * aimed at nit. Output from wilma will be delivered back to the initiator.
- *
- * Note that the ^M's in this message must be actual control-M's in the
- * file -- most of the parsing uses 015 as the line terminator, and tcpf
- * does not deal with NVT or CRLF issues. It turns out that our exec
- * uses 015 or 012 as a line terminator, so the some of the commands can
- * have normal newlines at the end.
- */
-
- #include <sys/types.h>
- #include <stdio.h>
- #include <sys/socket.h>
- #include <netdb.h>
- #include <netinet/in.h>
- #include <arpa/inet.h>
- #include <signal.h>
- #include <setjmp.h>
- #include <sys/time.h>
- #include <sys/ioctl.h>
- #include <errno.h>
-
- #define TRUE 1
- #define FALSE 0
-
- #define LF 0xA
- #define CR 0xD
-
- /* Accommodate worst case for stupid printers (-r switch) */
- #define TCPFBUFSIZ (BUFSIZ * 2)
-
- int debug; /* -d switch */
- int eofmode; /* -e switch */
- int timeoutmode; /* -t switch */
- int crlfmode; /* -r switch */
- int timeoutlen;
- int openwait; /* -w switch */
- int openwaitlen;
-
- struct sockaddr_in sock;
- jmp_buf terminate;
- unsigned char netbuffer[BUFSIZ];
- unsigned char netoutbuffer[TCPFBUFSIZ];
- unsigned char ttybuffer[BUFSIZ];
- int bytesread, byteswrote;
-
- void crashed();
- void transfer();
-
- /*
- * main
- * Parse command line and set up transfer.
- *
- * tcpf [-d -e -r -t -w] host port
- *
- * -d Enable debugging output.
- * -e Send CTRL/D over TCP side when EOF received on stdin.
- * This makes PostScript printers very happy.
- * -r Send a <CR><LF> pair to stream for every <LF> given
- * in stdin.
- * -t[time] Timeout in seconds. Default is 5 min.
- * -w[time] Wait for Time while opening connection too.
- * host Host name, either symbolic or dotted decimal.
- * port Decimal TCP port number. No defaults.
- */
-
- main(argc, argv)
- int argc;
- char *argv[];
- {
- char *hostname;
- unsigned short port;
- int conn;
-
- debug = FALSE;
- eofmode = FALSE;
- timeoutmode = FALSE;
- crlfmode = FALSE;
- argc--; argv++;
- while (argc > 1 && (argv[0][0] == '-')) {
- switch (argv[0][1]) {
- case 'd':
- case 'D':
- debug = TRUE;
- break;
- case 'e':
- case 'E':
- eofmode = TRUE;
- break;
- case 'r':
- case 'R':
- crlfmode = TRUE;
- break;
- case 't':
- case 'T':
- timeoutmode = TRUE;
- timeoutlen = atoi(&argv[0][2]);
- if (timeoutlen == 0)
- timeoutlen = 300;
- break;
- case 'w':
- case 'W':
- openwait = TRUE;
- openwaitlen = atoi(&argv[0][2]);
- if (openwaitlen <= 0)
- openwaitlen = 300;
- break;
- default:
- fprintf(stderr, "Usage: tcpf [-d -e -r -t[sec] -w[sec]] host port\n");
- exit(1);
- }
- argc--; argv++;
- }
- if (argc != 2) {
- fprintf(stderr, "Usage: tcpf [-d -e -t[sec] -w[sec]] host port\n");
- exit(1);
- }
- hostname = argv[0];
- port = atoi(argv[1]);
- if (!setup_socket(&sock, hostname, port))
- exit(1);
- if (debug) {
- fprintf(stderr, "Connecting to \"%s, %d\"... ",
- inet_ntoa(sock.sin_addr), ntohs(sock.sin_port));
- fflush(stderr);
- }
- conn = setup_connection(&sock);
- if (conn < 0)
- exit(1);
- signal(SIGPIPE, crashed);
- if (setjmp(terminate) == 0)
- transfer(conn);
- close(conn);
- if (debug)
- fprintf(stderr, "\nNet connection closed: %d bytes written, %d read\n",
- byteswrote, bytesread);
- exit(0);
- }
-
- /*
- * setup_socket
- * Turn a hostname string and port into a sockaddr structure.
- * Returns TRUE or FALSE.
- */
-
- int
- setup_socket(soc, host, port)
- struct sockaddr_in *soc;
- char *host;
- unsigned short port;
- {
- struct hostent *entry;
-
- soc->sin_family = AF_INET;
- soc->sin_port = htons(port);
- soc->sin_addr.s_addr = inet_addr(host);
- if (soc->sin_addr.s_addr != -1)
- return(TRUE);
- entry = gethostbyname(host);
- if (entry && entry->h_addr && (entry->h_addrtype == AF_INET)) {
- bcopy(entry->h_addr,&soc->sin_addr.s_addr, entry->h_length);
- /* strncpy(&soc->sin_addr.s_addr, entry->h_addr, entry->h_length); */
- return(TRUE);
- }
- fprintf(stderr, "Unknown hostname or address - \"%s\"\n", host);
- return(FALSE);
- }
-
- /*
- * crashed
- * Come here if connection failed
- */
-
- void
- crashed()
- {
- if (debug)
- perror("tcpf lost connection");
- longjmp(terminate, 1);
- }
-
- /*
- * setup_connection
- * Open a TCP connection to the specified address
- */
-
- int
- setup_connection(soc)
- struct sockaddr_in *soc;
- {
- int conn;
- int i;
-
- conn = socket(AF_INET, SOCK_STREAM, 0);
- if (conn < 0) {
- if (debug)
- perror("socket");
- return(-1);
- }
- if (openwait) {
- for (i=0; i < openwaitlen; i++) {
- if (connect(conn,soc, sizeof(*soc)) >= 0)
- break;
- if (i==0 && debug) {
- fprintf(stderr,"Waiting... ");
- fflush(stderr);
- }
- sleep(1);
- close(conn);
- conn = socket(AF_INET, SOCK_STREAM, 0);
- if (conn < 0) {
- if (debug)
- perror("socket");
- return(-1);
- }
- }
- if (i >= openwaitlen) {
- if (debug)
- perror("connect");
- return(-1);
- }
- } else if (connect(conn, soc, sizeof(*soc)) < 0) {
- if (debug)
- perror("connect");
- return(-1);
- }
- if (debug)
- fprintf(stderr, "Open\n");
- return(conn);
- }
-
- /*
- * transfer
- * Write data from stdin to the specified connection.
- * Read data from connection to stdout.
- */
-
- void
- transfer(net)
- int net;
- {
- int tib, nib, count;
- unsigned char *tibptr, *nibptr, *stiptr, *stoptr;
- int nready, readmask, writemask;
- int stdinmask, stdoutmask, netinmask, netoutmask;
- struct timeval timeout;
- int neteof, stdeof, nonblock;
-
- bytesread = byteswrote = 0;
- tib = nib = 0;
- neteof = stdeof = FALSE;
- stdinmask = (1 << fileno(stdin));
- stdoutmask = (1 << fileno(stdout));
- netinmask = (1 << net);
- netoutmask = (1 << net);
- timeout.tv_sec = timeoutlen;
- timeout.tv_usec = 0;
- nonblock = 1;
- ioctl(net, FIONBIO, &nonblock);
- setbuf(stdin, NULL);
- setbuf(stdout, NULL);
- while (TRUE) {
- if ((tib == 0) && (nib == 0) && (neteof == TRUE) && (stdeof == TRUE))
- return;
- readmask = 0;
- if (tib == 0)
- readmask |= (stdeof ? 0 : stdinmask);
- if (nib == 0)
- readmask |= (neteof ? 0 : netinmask);
- writemask = 0;
- if (tib)
- writemask |= netoutmask;
- if (nib)
- writemask |= stdoutmask;
- nready = select(8*sizeof(int), &readmask, &writemask, NULL,
- (timeoutmode ? &timeout : NULL));
- if (debug)
- printf("\nnready %d, rmask %4x, wmask %4x",
- nready, readmask, writemask);
- if (nready <= 0)
- /* exit if timeout or eror */
- return;
-
- /*
- * First we try reading from the net
- */
- if ((nib == 0) && (readmask & netinmask) && (neteof == FALSE)) {
- nibptr = netbuffer;
- nib = read(net, nibptr, BUFSIZ);
- if (debug)
- fprintf(stderr,"\nRead %d bytes from net", nib);
- if ((nib == 0) || (nib == -1)) {
- nib = 0;
- neteof = TRUE;
- }
- else {
- bytesread += nib;
- if (eofmode && (netbuffer[nib-1] == '\004')) {
- netbuffer[--nib] = '\000';
- neteof = TRUE;
- }
- }
- }
-
- /*
- * If we have input from the net, try writing it to standard out.
- */
- if ((nib > 0) && (writemask & stdoutmask)) {
- count = write(fileno(stdout), nibptr, nib);
- if (debug)
- fprintf(stderr,"\nWrote %d bytes to stdout", count);
- if ((count == nib) || (count < 0))
- nib = 0;
- else {
- nibptr += count;
- nib -= count;
- }
- }
-
- /*
- * Now read from standard input. If we read EOF and the user
- * wants us to pass it through, send a CTRL/D. This is very
- * useful when dealing with PostScript printers that require
- * the last character of a job be CTRL/D.
- */
- if ((tib == 0) && (readmask & stdinmask) && (stdeof == FALSE)) {
- tibptr = ttybuffer;
- tib = read(fileno(stdin), tibptr, BUFSIZ);
- if (debug)
- fprintf(stderr,"\nRead %d bytes from stdin", tib);
- if ((tib == 0) || (tib == -1)) {
- if ((tib == 0) && (eofmode == TRUE) && byteswrote) {
- if (debug)
- fprintf(stderr, " -- sending EOF character");
- tib = 1;
- ttybuffer[0] = '\004';
- }
- else
- tib = 0;
- stdeof = TRUE;
- }
- /*
- * Convert <LF> to <CR><LF>. Preexisting <CR><LF> pairs not hurt,
- * as <CR><CR><LF> has same effect. Uses this slow loop, rather
- * than "memccpy", because the memory routines do not exist on
- * all Unixes.
- */
- if (crlfmode) {
- for (stiptr = ttybuffer, stoptr = netoutbuffer;
- ((stiptr - ttybuffer) < tib);) {
- if (*stiptr == LF) {
- *stoptr++ = CR;
- }
- *stoptr++ = *stiptr++;
- }
- tib = (stoptr - netoutbuffer);
- tibptr = netoutbuffer;
- }
- }
-
- /*
- * If we have input from standard input, try writing to the net
- */
- if ((tib > 0) && (writemask & netoutmask)) {
- errno = 0;
- count = write(net, tibptr, tib);
- if (debug)
- fprintf(stderr,"\nWrote %d bytes to net", count);
- if (count < 0) {
- if ((errno != ENOBUFS) && (errno != EWOULDBLOCK))
- longjmp(terminate, -1);
- count = 0;
- }
- byteswrote += count;
- tibptr += count;
- tib -= count;
- }
-
- /*
- * If we hit EOF on either side, maybe set EOF on other side.
- */
- if ((stdeof == TRUE) && ((readmask & netinmask) == 0)&&(!timeoutmode))
- neteof = TRUE;
- if ((neteof == TRUE) && ((readmask & stdinmask) == 0)&&(!timeoutmode))
- stdeof = TRUE;
- }
- }
-